﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CQLQueries.Analyzers
{
    public class CQL_Semantic
    {
        // шаблон "одиночка"
        private static CQL_Semantic instance;

        /// <summary>
        /// Единственный экземпляр класса контроллера
        /// </summary>
        public static CQL_Semantic Instance
        {
            get
            {
                if (instance == null)
                    instance = new CQL_Semantic();
                return instance;
            }
        }

        public Dictionary<string, string> psevdonims = new Dictionary<string, string>();

        public CQL_Semantic()
        {
            PsevdonimsInit();
        }

        private void PsevdonimsInit()
        {
            psevdonims.Add("ID",              "IdElement");
            psevdonims.Add("ProcedureHeader", "OuterProcedureHeadElement");
            psevdonims.Add("FunctionHeader",  "OuterFunctionHeadElement");
            psevdonims.Add("MethodHeader",    "OuterMethodElement");
        }

        public event EventHandler<ErrorArgs> ErrorOccuredEvent;

        public List<QueryData> Запросы;
        public QueryData запрос;

        private void ErrorSemantic(String message)
        {
            if (ErrorOccuredEvent != null)
                ErrorOccuredEvent(this, new ErrorArgs(message,
                    CQL_LA.Instance.CurPos - CQL_LA.Instance.CurText.Length,
                    "Семантическая ошибка", CQL_LA.Instance.BeginToken, CQL_LA.Instance.EndToken));
        }

        private string str2;
        public bool ПроверкаSelect(String str)
        {
            //if (!psevdonims.ContainsKey(str))
            //{
            //    ErrorSemantic("Для " + str + " нет псевдонима.");
            //    return false;
            //}
            //str = psevdonims[str]; ;
            //str2 = str;
            //if (str.IndexOf('.') != -1)
            //{
            //    ErrorSemantic("В подвыражении SELECT указан член класса.");
            //    return false;
            //}
            switch (запрос.TestSelectType(str))
            {
                case -1:
                    ErrorSemantic("В сборке daSyntaxTree нет класса " + str + ".");
                    return false;
                case -2:
                    ErrorSemantic("Для " + str + " нет псевдонима.");
                    return false;
                case -3:
                    ErrorSemantic("Не задан идентификатор в подвыражении SELECT.");
                    return false;
                case -4:
                    ErrorSemantic(str + " не являеться подмножеством " + запрос.SelectTypePar.Str);
                    return false;
                case 0: return true;
                default: return false;
            }
            return true;
            //switch (запрос.TestSelectType(str))
            //{
            //    case -1:
            //        ErrorSemantic("В сборке daSyntaxTree нет класса " + str + ".");
            //        return false;
            //    case -2:
            //        ErrorSemantic("В запросе уже указан идентификатор " + str + ".");
            //        return false;
            //}
            //return true;
        }

        public bool TestTypeOfValue(CQLTokens op, Type TypeOfValueLeft, Type TypeOfValueRight)
        {
            if (TypeOfValueLeft.Name == TypeOfValueRight.Name)
            {
                if (TypeOfValueLeft.Name == "Double")
                {
                    switch (op)
                    {
                        case CQLTokens.Like:
                        case CQLTokens.Not:
                            ErrorSemantic("Попытка произвести не допустимую операцию с операндами одинакового типа.");
                            return false;
                        default: return true;
                    }                    
                }
                else if (TypeOfValueLeft.Name == "String")
                {
                    switch (op)
                    {
                        case CQLTokens.Less:
                        case CQLTokens.LessOrEqual:
                        case CQLTokens.More:
                        case CQLTokens.MoreOrEqual:
                        case CQLTokens.Add:
                        case CQLTokens.Sub:
                        case CQLTokens.Mul:
                        case CQLTokens.Div:
                        case CQLTokens.Mod:
                            ErrorSemantic("Попытка произвести не допустимую операцию с операндами одинакового типа.");
                            return false;
                        default: return true;
                    }       
                }
                else
                {
                    ErrorSemantic("Попытка произвести не допустимую операцию с операндами одинакового типа.");
                    return false;
                }
            }
            else
            {
                ErrorSemantic("Попытка произвести операцию с операндами разных типов.");
                return false;
            }
            return false;
        }
        
        public bool ПроверкаWhereПодтип(String str, Type owner, out Type t)
        {
            t = null;
            if (!запрос.AddParam(owner, str))
            {
                ErrorSemantic(str + " не являеться подмножеством " + str2);
                return false;
            }
            String TypeName = запрос.Params[запрос.IndLastParam].EndType.Name;
            if ((TypeName == "Int32") || (TypeName == "String"))
            {
                t = запрос.Params[запрос.IndLastParam].EndType;
                return true;
            }
            else
            {
                ErrorSemantic(str + " имеет не допустимый тип данных.");
                return false;
            }
        }

        public bool ПроверкаWhereСравнение(bool left, bool right, CQLTokens Rel)
        {            
            if ((!left && right) || (left && !right))
            {
                ErrorSemantic("Попытка сравнить операнды разных типов данных.");
                return false;
            }
            if (left)
            {
                switch (Rel)
                {
                    case CQLTokens.Like:
                    case CQLTokens.Not:
                        ErrorSemantic("Попытка применить недопустимые операции сравнения числового типа данных");
                        return false;
                    default:
                        return true;
                }
            }
            else
            {
                switch (Rel)
                {
                    case CQLTokens.Equal:
                    case CQLTokens.NotEqual:
                    case CQLTokens.Like:
                    case CQLTokens.Not:
                        return true;
                    default:
                        ErrorSemantic("Попытка применить недопустимые операции сравнения для строкового типа данных");
                        return false;
                }
            }            
        }
    }
}
